import hashlib
import json
import uuid

import torch
from django.conf import settings
from django.http import JsonResponse, StreamingHttpResponse
from django.views.decorators.csrf import csrf_exempt
from docxtpl import DocxTemplate
from torch import nn
from torch.nn import Conv1d, MaxPool1d, BatchNorm1d, ReLU

from Detection.utils.bispectrum import *
from Detection.utils.feature_extraction import get_features
from Detection.utils.heart_sound_segmentation2 import *
from Detection.utils.utils import *
from Detection.utils.wavelet_denoising import wavelet_denoising
from MedicRecord.models import MedicRecord
from PatientProfile.models import PatientProfile
from PatientRecord.models import PatientRecord


# 获得随机的文档名字
def get_random_str():
    uuid_val = uuid.uuid4()
    uuid_str = str(uuid_val).encode('utf-8')
    md5 = hashlib.md5()
    md5.update(uuid_str)
    return md5.hexdigest()


def LoadFile(request):
    if request.method == "GET":
        result = {'code': 400, 'error': '上传音频方式错误'}
        return JsonResponse(result)
    else:
        json_str = request.body
        json_obj = json.loads(json_str)
        file_path = json_obj.get("wave_path")
        # print("file_path ",file_path)
        try:
            # 源数据处理
            WAVE = wave.open(file_path)
            a = WAVE.getparams().nframes  # 帧总数
            f = WAVE.getparams().framerate  # 采样频率

            bitdeep = 16
            downsampling_ratio = f // 1000
            time = float(a / f)
            if time >= 10:
                second = 10
            else:
                second = time

            # 源数据处理
            wave_data, wave_time, framerate = read_part_wavefile(file_path, second, downsampling_ratio)

            # 滤波处理
            filteredData = butterworth_bandpass_filtering(wave_data, framerate, downsampling_ratio)

            # 降噪处理
            denoisedData, usecoeffs = wavelet_denoising(filteredData, 'db4')

            # 心音分割
            standard_data = StandardScaler(denoisedData)
            normalized_data = MaxMinNormalization(standard_data)
            timesE, envelope = find_envelope(normalized_data, downsampling_ratio, framerate)
            intervals, maximum = envelope_segmentation(timesE, envelope, second)  # time为时间轴，envelope为包络值
            state_data = get_label(intervals, timesE, wave_time, downsampling_ratio, framerate)

            # 新建 amplitude.csv & label.csv
            amplitude_path = os.path.join(settings.CSVS_DIR, "amplitude.csv")
            label_path = os.path.join(settings.CSVS_DIR, "label.csv")

            # print(os.path.getC)
            try:
                amplitude_array = np.array(normalized_data).reshape(1, -1)
                amplitude_df = pd.DataFrame(amplitude_array)
                amplitude_df.to_csv(amplitude_path, index_label=False, index=False, header=False)
            except Exception as e:
                if not os.path.exists(amplitude_path):
                    print("amplitude 不存在！")
                result = {'code': 400, 'msg': '特征提取中...振幅文件保存失败！'}
                return JsonResponse(result)

            try:
                state_array = np.array(state_data).reshape(1, -1)
                state_df = pd.DataFrame(state_array)
                state_df.to_csv(label_path, index_label=False, index=False, header=False)
            except Exception as e:
                if not os.path.exists(label_path):
                    print("label不存在！")
                result = {'code': 400, 'msg': '特征提取中...状态文件保存失败！'}
                return JsonResponse(result)

            extracted_features = get_features(amplitude_path, label_path)

            # 计算二阶谱系数矩阵
            freq1, freq2, bi_spectrum = polycoherence(
                normalized_data,
                nfft=1024,
                nperseg=256,
                noverlap=100,
                fs=1000,
                norm=None)

            bi_spectrum = np.array(abs(bi_spectrum))  # calculate bi_spectrum
            row, col = bi_spectrum.shape

            bi_spectrum_list = []
            for i in range(row):
                cur_list = []
                for j in range(col):
                    cur_list.append((float)(bi_spectrum[i][j]))
                bi_spectrum_list.append(cur_list)

            # 计算梅尔频谱系数

            # 样本预测

            class CNN_BiLSTM(nn.Module):
                def __init__(self, input_size, hidden_size, num_layers, num_classes):
                    super(CNN_BiLSTM, self).__init__()
                    self.num_layers = num_layers
                    self.hidden_size = hidden_size
                    self.cnn = nn.Sequential(
                        Conv1d(in_channels=input_size, out_channels=64, kernel_size=CONV_KERNAL_SIZE),
                        MaxPool1d(kernel_size=MAXPOOL_KERNAL_SIZE),
                        ReLU(),
                        BatchNorm1d(num_features=64),
                        Conv1d(in_channels=64, out_channels=64, kernel_size=CONV_KERNAL_SIZE),
                        MaxPool1d(kernel_size=MAXPOOL_KERNAL_SIZE),
                        ReLU(),
                        BatchNorm1d(num_features=64),
                    )
                    self.encoder = nn.LSTM(
                        input_size=64,
                        hidden_size=hidden_size,
                        num_layers=num_layers,
                        bidirectional=True,
                        batch_first=True
                    )
                    self.decoder = nn.Linear(hidden_size * 2, num_classes)
                    self.dropout = nn.Dropout(0.5)

                def forward(self, x):
                    x = x.transpose(1, 2).contiguous()
                    x = self.cnn(x)
                    x = x.transpose(1, 2).contiguous()

                    device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
                    h0 = torch.zeros(self.num_layers * 2, x.size(0), self.hidden_size)
                    c0 = torch.zeros(self.num_layers * 2, x.size(0), self.hidden_size)
                    h0, c0 = h0.to(device), c0.to(device)
                    lstm_out, (hidden, cell) = self.encoder(x, (h0, c0))
                    hidden = self.dropout(torch.cat((hidden[-2, :, :], hidden[-1, :, :]), dim=1))
                    decoded_outs = self.decoder(hidden)
                    outs = nn.functional.softmax(decoded_outs, dim=1)

                    return outs

            NUM_CLASSES = 2
            HIDDEN_SIZE = 128
            NUM_LAYERS = 2
            INPUT_SIZE = 13
            CONV_KERNAL_SIZE = 5
            MAXPOOL_KERNAL_SIZE = 2

            device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
            model_ft = CNN_BiLSTM(input_size=INPUT_SIZE, hidden_size=HIDDEN_SIZE, num_layers=NUM_LAYERS,
                                  num_classes=NUM_CLASSES)
            for param in model_ft.parameters():
                param.requires_grad = False

            cnn_lstm_weights_path = os.path.join(settings.CNN_LSTM_WEIGHTS_DIR,
                                                 "smote_crnn_lstm200_layers2_HiddenSize128_StepLr150.9_batch64activation_func_relu.pth")
            model_ft.load_state_dict(torch.load(cnn_lstm_weights_path, map_location=torch.device('cpu')))
            model_ft.to(device)
            model_ft.eval()

            feature_mfcc = mfcc(normalized_data, samplerate=framerate / downsampling_ratio)
            feature_mfcc_array = np.array(feature_mfcc)
            seq_lens, dims = feature_mfcc_array.shape
            feature_mfcc_batch = feature_mfcc_array.reshape(-1, seq_lens, dims)
            feature_mfcc_batch = torch.from_numpy(feature_mfcc_batch)
            feature_mfcc_batch = feature_mfcc_batch.to(torch.float32)
            feature_mfcc_batch = feature_mfcc_batch.to(device)
            output = model_ft(feature_mfcc_batch).numpy().squeeze(0)
            cnn_lstm_predict = []
            for i in range(2):
                cnn_lstm_predict.append(float(output[i]))

            # 数据打包
            wavefile_data = []
            filtered_data = []
            denoised_data = []
            status_data = []

            if second == 10:
                len = 4000
            else:
                len = second * 1000

            k = (int)(np.max(np.array(denoisedData)) / 4)
            print(k)

            for i in range(0, len, 2):
                org_data = []
                org_data.append((float)(round(wave_time[i], 3)))
                org_data.append((float)(wave_data[i]))
                wavefile_data.append(org_data)

                filt_data = []
                filt_data.append((float)(round(wave_time[i], 3)))
                filt_data.append((int)(round(filteredData[i], 3)))
                filtered_data.append(filt_data)

                denois_data = []
                denois_data.append((float)(round(wave_time[i], 3)))
                denois_data.append((int)(round(denoisedData[i], 3)))
                denoised_data.append(denois_data)

                cur_state = []
                cur_state.append((float)(round(wave_time[i], 3)))
                cur_state.append(state_data[i] * k)
                status_data.append(cur_state)

            # 这个地方不能传入double类型数据
            result = {'code': 200, 'msg': '音频文件加载成功！', 'wave_data': wavefile_data, \
                      'filtered_data': filtered_data, 'denoised_data': denoised_data, \
                      'status_data': status_data, 'features': extracted_features, 'bi_spectrum': bi_spectrum_list, \
                      'time': time, 'fs': f, 'downsampling_ratio': downsampling_ratio, 'bitdeep': bitdeep, \
                      'cnn_lstm_p': cnn_lstm_predict[0]}
            return JsonResponse(result, safe=False)
        except Exception as e:
            print(e)
            result = {'code': 400, 'msg': '音频加载失败！'}
            return JsonResponse(result)


def DenoiseFile(request):
    pass


@csrf_exempt
def DownloadFile(request):
    filename = request.GET.get('filename')
    baseUrl = os.path.join(settings.MEDIA_ROOT, 'words')
    filepath = os.path.join(baseUrl, filename)
    fp = open(filepath, 'rb')
    response = StreamingHttpResponse(fp)

    print('headers: ', request.headers.get('Origin'))
    response['Access-Control-Allow-Origin'] = request.headers.get('Origin')
    response['Content-Type'] = 'application/octet-stream'
    response['Content-Disposition'] = 'attachment;filename="%s"' % filename
    print('response: ', response['Access-Control-Allow-Origin'])
    return response


def GenerateReportWord(request):
    if request.method == 'GET':
        result = {'code': 400, 'msg': '请求方式错误'}
        return JsonResponse(result)
    else:
      try:
          recordId = request.POST.get('recordId')

          record = PatientRecord.objects.get(id__exact=recordId)
          patient = PatientProfile.objects.get(id__exact=record.patientId)

          a1 = request.POST.get('a1')
          a2 = request.POST.get('a2')
          a3 = request.POST.get('a3')
          a4 = request.POST.get('a4')

          b1 = request.POST.get('b1')
          b2 = request.POST.get('b2')
          b3 = request.POST.get('b3')
          b4 = request.POST.get('b4')

          c1 = request.POST.get('c1')
          c2 = request.POST.get('c2')
          c3 = request.POST.get('c3')
          c4 = request.POST.get('c4')

          d1 = request.POST.get('d1')
          d2 = request.POST.get('d2')

          baseUrl = os.path.join(settings.MEDIA_ROOT, 'words')
          assetUrl = os.path.join(baseUrl, 'ReportTemplate.docx')
          tpl = DocxTemplate(assetUrl)


          context = {'a1': a1, 'a2': a2, 'a3': a3, 'a4': a4, 'b1': b1, 'b2': b2, 'b3': b3, 'b4': b4,
                     'c1': c1, 'c2': c2, 'c3': c3, 'c4': c4, 'd1': d1, 'd2': d2,
                     'name': patient.name, 'nation': patient.nation, 'work': patient.work, 'age': patient.age,
                     'address': patient.address, 'card': patient.card, 'phone': patient.phone,
                     'gender': '男' if patient.gender == 1 else '女', 'time': record.reserve_time}

          tpl.render(context)
          reportName = get_random_str() + '.docx'

          tpl.save(os.path.join(baseUrl, reportName))
          record.reportWordName = reportName
          record.save()

          return JsonResponse({'code': 200, 'msg': '生成文档成功'})

      except Exception as e:
          result = {'code': 400, 'msg': '生成文档失败'}
          return JsonResponse(result)


def GenerateMedicWord(request):
    if request.method == 'GET':
        result = {'code': 400, 'msg': '请求方式错误'}
        return JsonResponse(result)
    else:
        try:
            recordId = request.POST.get('recordId')
            medicRecord = MedicRecord.objects.get(recordId__exact=recordId)
            print(request.POST)
            record = PatientRecord.objects.get(id__exact=recordId)
            patient = PatientProfile.objects.get(id__exact=record.patientId)
            a1 = request.POST.get('a1')
            a2 = request.POST.get('a2')
            a3 = request.POST.get('a3')
            a4 = request.POST.get('a4')
            a5 = request.POST.get('a5')
            a6 = request.POST.get('a6')

            medicRecord.clinic = a2
            medicRecord.chief_complaint = a3
            medicRecord.medical_history = a4
            medicRecord.proposals = a5
            medicRecord.diagnosis_result = a6
            medicRecord.save()

            baseUrl = os.path.join(settings.MEDIA_ROOT, 'words')
            assetUrl = os.path.join(baseUrl, 'MedicTemplate.docx')
            tpl = DocxTemplate(assetUrl)

            context = {'a1': a1, 'a2': a2, 'a3': a3, 'a4': a4, 'a5': a5, 'a6': a6,
                       'name': patient.name, 'nation': patient.nation, 'work': patient.work, 'age': patient.age,
                       'address': patient.address, 'card': patient.card, 'phone': patient.phone,
                       'gender': '男' if patient.gender == 1 else '女', 'time': record.reserve_time}
            tpl.render(context)

            medicName = get_random_str() + '.docx'

            tpl.save(os.path.join(baseUrl, medicName))
            record.medicalWordName = medicName
            record.save()
            if record.reportWordName:
                record.isFinished = 1
                record.save()

            return JsonResponse({'code': 200, 'msg': '生成文档成功'})
        except Exception as e:
            result = {'code': 400, 'msg': '生成文档失败'}
            return JsonResponse(result)
